package com.maozzi.service.acl.business;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.maozzi.api.acl.AclService;
import com.maozzi.api.acl.model.AclUserDTO;
import com.maozzi.api.acl.model.PermissionDTO;
import com.maozzi.api.acl.model.RoleDTO;
import com.maozzi.api.user.UserService;
import com.maozzi.api.user.model.UserDTO;
import com.maozzi.common.enums.BizCodeEnum;
import com.maozzi.common.exception.BizException;
import com.maozzi.common.model.PageDTO;
import com.maozzi.common.model.PageParam;
import com.maozzi.common.util.CloneUtil;
import com.maozzi.common.util.ParamCheckUtil;
import com.maozzi.service.acl.domain.PermissionDomainService;
import com.maozzi.service.acl.domain.RoleDomainService;
import com.maozzi.service.acl.domain.RolePermissionDomainService;
import com.maozzi.service.acl.domain.UserRoleDomainService;
import com.maozzi.service.acl.repository.model.Permission;
import com.maozzi.service.acl.repository.model.Role;
import com.maozzi.service.acl.repository.model.RolePermission;
import com.maozzi.service.acl.repository.model.UserRole;

import javax.annotation.Resource;

import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * 权限 - 角色
 *
 * @author maozi
 */
@Service
public class AclServiceImpl implements AclService {

    @Resource
    private UserService userService;
    @Resource
    private UserRoleDomainService userRoleDomainService;
    @Resource
    private RoleDomainService roleDomainService;
    @Resource
    private RolePermissionDomainService rolePermissionDomainService;
    @Resource
    private PermissionDomainService permissionDomainService;

    @Override
    public AclUserDTO getAclUser(Long userId) throws BizException {
        UserDTO userDTO = userService.getUser(userId);

        AclUserDTO dto = CloneUtil.to(userDTO, AclUserDTO.class);
        dto.setRoles(this.getUserRole(userId));
        dto.setPermissions(this.getUserPermission(userId));

        return dto;
    }

    @Override
    public List<RoleDTO> getUserRole(Long userId) throws BizException {
        List<UserRole> userRoleList = userRoleDomainService.getUserRole(userId);

        return CloneUtil.toList(userRoleList, RoleDTO.class);
    }

    @Override
    public List<PermissionDTO> getUserPermission(Long userId) throws BizException {
        List<PermissionDTO> permissionList = new ArrayList<>();

        List<UserRole> userRoleList = userRoleDomainService.getUserRole(userId);
        List<Long> roleIds = userRoleList.stream().map(UserRole::getRoleId).distinct().collect(Collectors.toList());
        if (ObjectUtils.isEmpty(roleIds)) {
            return permissionList;
        }

        List<RolePermission> rolePermissionList = rolePermissionDomainService.getRolePermissions(roleIds);
        List<Long> permissionIds = rolePermissionList.stream().map(RolePermission::getPermissionId).collect(Collectors.toList());
        if (ObjectUtils.isEmpty(permissionIds)) {
            return permissionList;
        }
        List<Permission> permissions = permissionDomainService.getPermissions(permissionIds);

        return CloneUtil.toList(permissions, PermissionDTO.class);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean putUserRole(AclUserDTO dto) throws BizException {
        Long userId = dto.getId();
        Long[] roleIds = dto.getRoles().stream().map(RoleDTO::getId).toArray(Long[]::new);

        Boolean isPut = userRoleDomainService.addUserRole(userId, roleIds);
        if (!isPut) {
            throw new BizException(BizCodeEnum.FAILED_OPERATION, "保存用户角色失败");
        }

        return Boolean.TRUE;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean editUserRole(Long userId, AclUserDTO dto) throws BizException {
        dto.setId(userId);

        this.delUserRole(userId);

        return this.putUserRole(dto);
    }

    @Override
    public void delUserRole(Long userId) throws BizException {
        Boolean isDel = userRoleDomainService.delUserRoleBatch(userId);
        if (!isDel) {
            throw new BizException(BizCodeEnum.FAILED_OPERATION, "删除用户角色失败");
        }
    }

    @Override
    public RoleDTO getRole(Long roleId) {
        Role role = roleDomainService.getRole(roleId);

        return CloneUtil.to(role, RoleDTO.class);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void addRole(RoleDTO dto) throws BizException {
        Role role = CloneUtil.to(dto, Role.class);

        roleDomainService.addRole(role);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean editRole(Long roleId, RoleDTO dto) throws BizException {
        Role role = CloneUtil.to(dto, Role.class);

        // 1. 编辑角色信息
        Boolean b = roleDomainService.editRole(roleId, role);
        if (!b) {
            throw new BizException(BizCodeEnum.FAILED_OPERATION, "角色编辑失败");
        }

        // 2. 编辑角色拥有的权限码信息
        List<PermissionDTO> permissions = dto.getPermissions();
        if (Objects.nonNull(permissions)) {
            List<Long> permissionIds = permissions.stream().map(PermissionDTO::getId).distinct().toList();
            if (permissions.isEmpty()) {
                // 为空说明，该角色清空了权限码
                rolePermissionDomainService.clear(roleId);
            } else {
                // 查询角色的权限码是否有变动
                List<RolePermission> rolePermissions = rolePermissionDomainService.getRolePermission(roleId);
                List<Long> rolePermissionIds = rolePermissions.stream()
                        .map(RolePermission::getPermissionId)
                        .distinct()
                        .collect(Collectors.toList());
                if (permissionIds.size() == rolePermissionIds.size()) {
                    rolePermissionIds.removeAll(permissionIds);
                    if (rolePermissionIds.isEmpty()) {
                        // 在两个集合size相等时，求差集为空，说明两个集合元素相等
                        return true;
                    }
                }

                // 先清空再重新插入数据
                rolePermissionDomainService.clear(roleId);
                rolePermissionDomainService.add(roleId, permissionIds);
            }
        }

        return true;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean delRole(Long roleId) throws BizException {
        Role role = new Role();
        role.setId(roleId);
        role.setDeleted(Boolean.TRUE);

        // 1. 检查角色是否正在被使用
        Boolean using = userRoleDomainService.using(roleId);
        if (using) {
            throw new BizException(BizCodeEnum.FAILED_OPERATION, "该角色正在被使用");
        }

        //  2. 删除角色
        Boolean b = roleDomainService.editRole(roleId, role);
        if (!b) {
            throw new BizException(BizCodeEnum.FAILED_OPERATION, "角色删除失败");
        }

        // 3. 清空角色的权限码
        rolePermissionDomainService.clear(roleId);

        return Boolean.TRUE;
    }

    @Override
    public PermissionDTO getPermission(Long permissionId) {
        Permission permission = permissionDomainService.getPermission(permissionId);

        return CloneUtil.to(permission, PermissionDTO.class);
    }

    @Override
    public PermissionDTO addPermission(PermissionDTO dto) {
        Permission entity = CloneUtil.to(dto, Permission.class);
        Permission permission = permissionDomainService.addPermission(entity);

        return CloneUtil.to(permission, PermissionDTO.class);
    }

    @Override
    public Boolean editPermission(Long permissionId, PermissionDTO dto) throws BizException {
        Permission permission = CloneUtil.to(dto, Permission.class);

        Boolean isEdit = permissionDomainService.editPermission(permissionId, permission);
        if (!isEdit) {
            throw new BizException(BizCodeEnum.FAILED_OPERATION, "权限编辑失败");
        }

        return Boolean.TRUE;
    }

    @Override
    public Boolean delPermission(Long permissionId) throws BizException {
        Permission permission = new Permission();
        permission.setId(permissionId);
        permission.setDeleted(Boolean.TRUE);

        Boolean isEdit = permissionDomainService.editPermission(permissionId, permission);
        if (!isEdit) {
            throw new BizException(BizCodeEnum.FAILED_OPERATION, "权限删除失败");
        }

        return Boolean.TRUE;
    }

    @Override
    public PageDTO<RoleDTO> rolePage(RoleDTO dto, PageParam pageParam) throws BizException {
        ParamCheckUtil.notNull(pageParam, "分页参数错误");

        Page<Role> page = new Page<>(pageParam.getPage(), pageParam.getPageSize());
        LambdaQueryWrapper<Role> query = Wrappers.lambdaQuery(Role.class)
                .eq(Role::getDeleted, Boolean.FALSE)
                .orderByDesc(Role::getId);
        if (Objects.nonNull(dto)) {
            // 名称
            String name = dto.getName();
            if (StringUtils.hasText(name)) {
                query.likeLeft(Role::getName, name);
            }
        }
        page = roleDomainService.rolePage(page, query);

        return CloneUtil.toPageDTO(page, RoleDTO.class);
    }

    @Override
    public PageDTO<PermissionDTO> permissionPage(PermissionDTO dto, PageParam pageParam) throws BizException {
        ParamCheckUtil.notNull(pageParam, "分页参数错误");

        Page<Permission> page = Page.of(pageParam.getPage(), pageParam.getPageSize());
        LambdaQueryWrapper<Permission> query = Wrappers.lambdaQuery(Permission.class)
                .eq(Permission::getDeleted, false)
                .orderByDesc(Permission::getId);
        if (Objects.nonNull(dto)) {
            // 权限码
            String code = dto.getCode();
            if (StringUtils.hasText(code)) {
                query.likeLeft(Permission::getCode, code);
            }
        }

        page = permissionDomainService.page(page, query);

        return CloneUtil.toPageDTO(page, PermissionDTO.class);
    }
}
